package com.junxonline.instant.admin.aspect;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.junxonline.instant.common.util.common.RedisUtils;
import com.junxonline.instant.dao.entity.model.device.Terminal;
import com.junxonline.instant.dao.entity.model.logDevice.LogDeviceOperation;
import com.junxonline.instant.dao.entity.vo.common.IdsVO;
import com.junxonline.instant.common.annotation.DeviceOperationLog;
import com.junxonline.instant.common.constant.CommonConstant;
import com.junxonline.instant.common.constant.RedisMqChannelConstant;
import com.junxonline.instant.common.entity.common.CommonResult;
import com.junxonline.instant.common.util.admin.TokenUtils;
import com.junxonline.instant.util.NetworkUtils;
import lombok.extern.log4j.Log4j2;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 终端操作日志切面类
 *
 * @author JunX
 * @date 2021-11-16
 */
@Log4j2
@Aspect
@Component
public class DeviceOperationLogAspect {

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    private TokenUtils tokenUtils;

    /**
     * 定义切入点为标识了注解@DeviceOperationLog的方法
     */
    @Pointcut(value = "@annotation(operationLog)")
    public void deviceOperationLog(DeviceOperationLog operationLog) {

    }

    /**
     * 前置通知
     *
     * @param joinPoint 切入点
     * @param operationLog 终端操作日志注解
     */
    @Before(value = "deviceOperationLog(operationLog)")
    public void before(JoinPoint joinPoint, DeviceOperationLog operationLog) {

    }

    /**
     * 后置通知
     *
     * @param joinPoint 切入点
     * @param operationLog 终端操作日志注解
     * @param result    公共响应对象
     */
    @AfterReturning(value = "deviceOperationLog(operationLog)", returning = "result")
    public void afterReturning(JoinPoint joinPoint, DeviceOperationLog operationLog, CommonResult result) {
        this.saveLog(joinPoint, operationLog, CommonConstant.SUCCESS);
    }

    /**
     * 异常通知
     *
     * @param joinPoint 切入点
     * @param operationLog 终端操作日志注解
     * @param e         异常
     */
    @AfterThrowing(value = "deviceOperationLog(operationLog)", throwing = "e")
    public void afterThrowing(JoinPoint joinPoint, DeviceOperationLog operationLog, Exception e) {
        this.saveLog(joinPoint, operationLog, CommonConstant.FAIL);
    }

    /**
     * 发送操作日志消息
     *
     * @param joinPoint 切入点
     * @param operationLog 终端操作日志注解
     * @param status    操作结果
     */
    private void saveLog(JoinPoint joinPoint, DeviceOperationLog operationLog, Integer status) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String token = request.getHeader("Authorization");
        if (!StrUtil.isBlank(token)) {
            Long userId = tokenUtils.getUserId(token);
            if (userId != null) {
                Object[] objects = joinPoint.getArgs();
                Signature signature = joinPoint.getSignature();
                MethodSignature methodSignature = (MethodSignature) signature;
                String[] parameterNames = methodSignature.getParameterNames();
                for (int i = 0; i < parameterNames.length; i++) {
                    if (parameterNames[i].equals("vo")) {
                        IdsVO<List<Long>> idsVO = (IdsVO<List<Long>>) objects[i];
                        JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(objects[i]));
                        jsonObject.remove("ids");
                        // Redis消息队列发送操作日志消息
                        Map<String, Object> data = new HashMap<>();
                        List<Long> ids = idsVO.getIds();
                        data.put("ids", ids);
                        LogDeviceOperation logDeviceOperation = new LogDeviceOperation();
                        logDeviceOperation.setName(operationLog.label());
                        logDeviceOperation.setType(operationLog.type().getValue());
                        logDeviceOperation.setUrl(request.getRequestURI());
                        logDeviceOperation.setCreateId(userId);
                        logDeviceOperation.setUserIp(NetworkUtils.getIpAddress(request));
                        logDeviceOperation.setStatus(status);
                        logDeviceOperation.setOperationTime(DateUtil.date());
                        logDeviceOperation.setParams(jsonObject.toJSONString());
                        data.put("logDeviceOperation", logDeviceOperation);
                        redisUtils.convertAndSend(RedisMqChannelConstant.KEY_REDIS_MSG_CHANNEL_LOG_DEVICE_OPERATION, JSON.toJSONString(data));
                    }
                    else if (parameterNames[i].equals("terminal")) {
                        Terminal terminal = (Terminal) objects[i];
                        JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(objects[i]));
                        // Redis消息队列发送操作日志消息
                        Map<String, Object> data = new HashMap<>();
                        List<Long> ids = new ArrayList<>();
                        ids.add(terminal.getId());
                        data.put("ids", ids);
                        LogDeviceOperation logDeviceOperation = new LogDeviceOperation();
                        logDeviceOperation.setName(operationLog.label());
                        logDeviceOperation.setType(operationLog.type().getValue());
                        logDeviceOperation.setUrl(request.getRequestURI());
                        logDeviceOperation.setCreateId(userId);
                        logDeviceOperation.setUserIp(NetworkUtils.getIpAddress(request));
                        logDeviceOperation.setStatus(status);
                        logDeviceOperation.setOperationTime(DateUtil.date());
                        logDeviceOperation.setParams(jsonObject.toJSONString());
                        data.put("logDeviceOperation", logDeviceOperation);
                        redisUtils.convertAndSend(RedisMqChannelConstant.KEY_REDIS_MSG_CHANNEL_LOG_DEVICE_OPERATION, JSON.toJSONString(data));
                    }
                }
            }
        }
    }

}
